<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Symbol</title>
	</head>
	<body>
		<p>ES5 的对象属性名都是字符串，这容易造成属性名的冲突。比如，你使用了一个他人提供的对象，但又想为这个对象添加新的方法（mixin
			模式），新方法的名字就有可能与现有方法产生冲突。如果有一种机制，保证每个属性的名字都是独一无二的就好了，这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol的原因。</p>
		<p>ES6 引入了一种新的原始数据类型Symbol，表示独一无二的值。它是 JavaScript
			语言的第七种数据类型，前六种是：undefined、null、布尔值（Boolean）、字符串（String）、数值（Number）、对象（Object）</p>
		<script type="text/javascript">
			let s = Symbol();
			console.log(typeof s); // "symbol"
			let s1 = Symbol('foo');
			console.log(s1); // Symbol(foo)
			console.log(s1.toString()); // "Symbol(foo)"

			let sym1 = Symbol();
			let sym2 = Symbol();
			let obj = {
				[sym1]: "aaa",
				[sym2](arg) {
					//... 
				}
			};
			// 注意，Symbol 值作为对象属性名时，不能用点运算符
			console.log(obj.sym1); // undefined
			console.log(obj[sym1]); // "aaa"

			// 由于以 Symbol 值作为键名，不会被常规方法遍历得到。我们可以利用这个特性，为对象定义一些非私有的、但又希望只用于内部的属性或方法
			let size = Symbol('size');
			class Collection {
				constructor() {
					this[size] = 0;
				}

				add(item) {
					this[this[size]] = item;
					this[size]++;
				}

				static sizeOf(instance) {
					return instance[size];
				}
			}

			let x = new Collection();
			Collection.sizeOf(x) // 0
			x.add('foo');
			Collection.sizeOf(x) // 1
			// 无法获取到size属性
			console.log(Object.keys(x)); // Array [ "0" ]
			console.log(Object.getOwnPropertyNames(x)); // Array [ "0" ]

			// 有时，我们希望重新使用同一个 Symbol 值，Symbol.for()方法可以做到这一点。它接受一个字符串作为参数，然后搜索有没有以该参数作为名称的 Symbol 值。如果有，就返回这个 Symbol 值，否则就新建一个以该字符串为名称的 Symbol 值，并将其注册到全局
			Symbol.for("bar") === Symbol.for("bar") // true
			Symbol("bar") === Symbol("bar") // false

			// 其他方法
			// Symbol.hasInstance判断该Symbol对象是否有实例
			// Symbol.match
			// Symbol.replace
			// Symbol.search
			// Symbol.split
			// ......
		</script>
	</body>
</html>
